/*
JETware Hands-free Extension provides additional features for Windows Mobile HFP support.
Copyright (C) 2004-2009 Jim Thatcher

This program is free software; you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; 
if not, write to the Free Software Foundation, Inc., 
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Source Control header

$Workfile: Contacts.cpp $
$Revision: 17 $
$Date: 7/19/07 1:13p $
*/
#include "stdafx.h"

#include <windows.h>
#include <initguid.h>
#include <objbase.h>
// #include <comutil.h>
#include <pimstore.h>
#include "BTHFExt.h"

IPOutlookApp * g_polApp = NULL;
IFolder * g_pContactsFolder = NULL;
int g_nNumContacts = 0;
int g_nCountNumbers = 0;
int nLastReadSIMEntry = -1;
// int nLastReadOLEntry = 0; FIX: 1.20: No longer used

typedef struct tagPBITEM
{
	BYTE type;
	WORD index;
} PBITEM;

typedef struct tagPHONELIST
{
	WORD count;
	DWORD time; // zero unless count time exceeded g_dwPBCountTimeout
	PBITEM entries[1];
} PHONELIST;

PHONELIST * olPhoneList = NULL;

typedef struct tagLOGLIST
{
	WCHAR			Number[32];
	int				IOM;
	tagLOGLIST *	Next;
} LOGLIST, FAR * LPLOGLIST;

LPLOGLIST LogList = NULL;

BOOL FindLogEntry(PTSTR szNumber)
{
	LPLOGLIST pCurr = LogList;
	while (pCurr != NULL)
	{
		if (lstrcmp(szNumber, pCurr->Number) == 0)
			return TRUE;
		pCurr = pCurr->Next;
	}
	return FALSE;
}

void DeleteLogList()
{
	LPLOGLIST pCurr = LogList;
	while (pCurr != NULL)
	{
		LogList = pCurr->Next;
		LocalFree(pCurr);
		pCurr = LogList;
	}
}

BOOL AddLogEntry(PTSTR szNumber, int IOM)
{
	LPLOGLIST pCurr = LogList;
	if (pCurr == NULL) // handle case where list is empty
	{
		LogList = (LPLOGLIST) LocalAlloc(LPTR, sizeof(LOGLIST));
		pCurr = LogList;
	}
	else
	{
		while (pCurr->Next != NULL)
			pCurr = pCurr->Next;
		pCurr->Next = (LPLOGLIST) LocalAlloc(LPTR, sizeof(LOGLIST));
		pCurr = pCurr->Next;
	}
	if (pCurr != NULL)
	{
		lstrcpy(pCurr->Number, szNumber);
		pCurr->IOM = IOM;
		pCurr->Next = NULL;
		return TRUE;
	}
	else
	{
#ifdef DEBUG
		LogString("Memory allocation failed in AddLogEntry", DEBUG_TAPI);
#endif
		return FALSE;
	}
}

void ResetContacts()
{
	if (olPhoneList != NULL)
	{
		// FIX: 1.31: If CountContacts() took longer than g_dwPCountTimeout seconds keep phonebook so it will be used for next connection
		if (olPhoneList->time == 0)
		{
			LocalFree(olPhoneList);
			olPhoneList = NULL;
		}
	}
	DeleteLogList();
	// nLastReadOLEntry = 0; //FIX: 1.20: No longer used
	nLastReadSIMEntry = -1;
}

BOOL InitializeOutlook()
{
	// if it's already initialized, just return
	if (g_polApp != NULL)
		return TRUE;
	// otherwise initialize COM
#ifdef DEBUG
	LogString("Initializing Outlook...\r\n", DEBUG_INFO);
#endif
	if (SUCCEEDED(CoInitializeEx( NULL, 0)))
	{
		// Now, let's get the main outlook application
		if (SUCCEEDED(CoCreateInstance(CLSID_Application, NULL, CLSCTX_INPROC_SERVER, 
			IID_IPOutlookApp, reinterpret_cast<void **>(&g_polApp))))
		{
			// login to the Pocket Outlook object model
			if(SUCCEEDED(g_polApp->Logon(NULL)))
			{
				IPOutlookItemCollection * pItemCol = NULL;

				if (SUCCEEDED(g_polApp->GetDefaultFolder(olFolderContacts, &g_pContactsFolder)))
				{
					IPOutlookItemCollection * pItemCol = NULL;
					if (SUCCEEDED(g_pContactsFolder->get_Items(&pItemCol)))
					{
						pItemCol->get_Count(&g_nNumContacts);
						pItemCol->Release();
						return TRUE;
					}
					// if Logon failed, cleanup POOM instance
					g_pContactsFolder->Release();
				}
			}
			g_polApp->Logoff();
			g_polApp->Release();
		}
		// and uninitialize COM
		CoUninitialize();
	}
	g_polApp = NULL;
	return FALSE;
}

void CleanupOutlook()
{
	if (g_polApp != NULL)
	{
		g_pContactsFolder->Release();
		g_polApp->Logoff();
		g_polApp->Release();
	}
	CoUninitialize();
	if (olPhoneList != NULL)
	{
		LocalFree(olPhoneList);
		olPhoneList = NULL;
	}
	DeleteLogList();
}

int StripPhoneNumber(TCHAR * pszIn, TCHAR * pszOut, int nLen)
{
	TCHAR * pszCur = pszIn;
	int nCount = 0;
	while((*pszCur) && (nCount < nLen-1))
	{
		if (((*pszCur >= '0') && (*pszCur <= '9')) || (*pszCur == '+')
			|| (*pszCur == '#') || (*pszCur == '*'))
		{
			*(pszOut+nCount++) = *pszCur;
		}
		pszCur++;
		// nCount++;
	}
	*(pszOut+nCount++) = 0;
	return nCount;
}

BOOL FindContact(LPTSTR lpNumber, LPTSTR lpName)
{
	int nNumberCount = 0;
	int nCurrOlRecord = 1;
	int nNumLen = lstrlen(lpNumber);
	BOOL bMatchContact = FALSE;
	// We only want to compare the back end of the number
	// TODO: Adjust number of digits based on country code
	LPTSTR lpShortNum = lpNumber;
	if (nNumLen > 7)
		lpShortNum = lpNumber+(nNumLen-7);
	// Initialize POOM, get first contact info
	if (InitializeOutlook())
	{
		// Open the Contacts folder
		IPOutlookItemCollection * pItemCol = NULL;

		if (SUCCEEDED(g_pContactsFolder->get_Items(&pItemCol)))
		{
			IContact * pContact = NULL;
			
			while (nCurrOlRecord <= g_nNumContacts)
			{
				if (SUCCEEDED(pItemCol->Item (nCurrOlRecord, reinterpret_cast<IDispatch**>(&pContact))))
				{
					BSTR bstrNumbers[4];
					// grab phone numbers
					pContact->get_HomeTelephoneNumber(&bstrNumbers[0]);
					pContact->get_MobileTelephoneNumber(&bstrNumbers[1]);
					pContact->get_BusinessTelephoneNumber(&bstrNumbers[2]);
					pContact->get_CarTelephoneNumber(&bstrNumbers[3]);

					for (int x=0; x < 3; x++)
					{
						if (!bMatchContact && (SysStringByteLen(bstrNumbers[x]) > 0))
						{
							TCHAR szTemp[32];
							StripPhoneNumber(bstrNumbers[x], szTemp, 32);

							if (wcsstr(szTemp, lpShortNum) != NULL)
							{
								bMatchContact = TRUE;
								BSTR bstrFirstName;
								BSTR bstrLastName;
								BSTR bstrFileAs;

								// grab properties
								pContact->get_FirstName(&bstrFirstName);
								pContact->get_LastName(&bstrLastName);
								pContact->get_FileAs(&bstrFileAs);

								if (SysStringByteLen(bstrFileAs) > 0)
								{
									lstrcpy(lpName, bstrFileAs);
								}
								else
								{
									lstrcpy(lpName, bstrFirstName);
									lstrcat(lpName, _T(" "));
									lstrcat(lpName, bstrLastName);
								}
								// TODO: Allow custom mask for Outlook names
								SysFreeString(bstrFirstName);
								SysFreeString(bstrLastName);
								SysFreeString(bstrFileAs);
							}
						}
						SysFreeString(bstrNumbers[x]);
					}
					// clean up
					pContact->Release();
					if (bMatchContact)
						break;
				}
				nCurrOlRecord++;
			}
			pItemCol->Release();
		}
	}
	return bMatchContact;
}

DWORD CountContacts()
{
	int nNumberCount = 0;
	int nCurrOlRecord = 1;
	int nMaxNumbers = 9;
	if (g_dwIncludeFax)
		nMaxNumbers += 2;
	DWORD dwStartTime = GetTickCount();
	// Initialize local list of number indices
	// TODO: Only do this once per session
	WORD wMaxCount = (g_dwMaxContacts[CONTACTS_ME] == (DWORD) NO_LIMIT) ? 4096 : (WORD) g_dwMaxContacts[CONTACTS_ME];
#ifdef DEBUG
	CHAR szLog[100];
	sprintf(szLog, "CountContacts attempting to allocate list for %i entries (reg max=%i)", wMaxCount, g_dwMaxContacts[CONTACTS_ME]);
	LogString(szLog, DEBUG_CONTACT_DETAIL);
#endif
	if (olPhoneList != NULL)
	{
		// FIX 1.31: Large phonebooks take too long to count, causing some devices to restart session after delayed response
		// The fix is the lowest code impact approach. If last count took more than g_dwPBCountTimeout seconds (default 5)
		//  ResetContacts() won't delete olPhoneList.  If it's been more than 30 minutes since phonebook was populated,
		// detete it anyway and let first read attempt fail.  Otherwise keep and use the phonebook from the last read.
		if ((olPhoneList->time) && (dwStartTime - olPhoneList->time) < 1800000) // 30min*60sec*1000ticks/sec
			return olPhoneList->count;
		else
			LocalFree(olPhoneList);
	}
	olPhoneList = (PHONELIST *) LocalAlloc(LPTR, sizeof(PHONELIST) + (wMaxCount * sizeof(PBITEM)));
	while (olPhoneList == NULL)
	{
		wMaxCount -= 16;
		if (wMaxCount <= 16)
		{
			wMaxCount = 0;
			return 0; // Couldn't allocate memory for index of numbers
		}
		olPhoneList = (PHONELIST *) LocalAlloc(LPTR, sizeof(PHONELIST) + (wMaxCount * sizeof(PBITEM)));
	}
#ifdef DEBUG
	sprintf(szLog, "Allocated list to %i entries: ptr=%8.8X, size=%8.8X", wMaxCount, olPhoneList, LocalSize(olPhoneList));
	LogString(szLog, DEBUG_CONTACT_DETAIL);
#endif
	olPhoneList->count = wMaxCount;
	// Initialize POOM, get first contact info
	if (InitializeOutlook())
	{
		// Open the Contacts folder
		IPOutlookItemCollection * pItemCol = NULL;

		if (SUCCEEDED(g_pContactsFolder->get_Items(&pItemCol)))
		{
			IContact * pContact = NULL;
			
			while ((nCurrOlRecord <= g_nNumContacts) && (nNumberCount < olPhoneList->count))
			{
#ifdef DEBUG_DETAIL
				sprintf(szLog, "Reading contact %i, current number count %i", nCurrOlRecord, nNumberCount);
				LogString(szLog, DEBUG_CONTACT_DETAIL);
#endif
				if (SUCCEEDED(pItemCol->Item (nCurrOlRecord, reinterpret_cast<IDispatch**>(&pContact))))
				{
					int nNumsReturned = 0;
					BSTR bstrCategories;
					BSTR bstrNumbers[11];
					BOOL bMatchContact = TRUE;
				
					// grab properties
					pContact->get_Categories(&bstrCategories);
					pContact->get_HomeTelephoneNumber(&bstrNumbers[0]);
					pContact->get_MobileTelephoneNumber(&bstrNumbers[1]);
					pContact->get_BusinessTelephoneNumber(&bstrNumbers[2]);
					pContact->get_CarTelephoneNumber(&bstrNumbers[3]);
					pContact->get_AssistantTelephoneNumber(&bstrNumbers[4]);
					pContact->get_Business2TelephoneNumber(&bstrNumbers[5]);
					pContact->get_Home2TelephoneNumber(&bstrNumbers[6]);
					pContact->get_PagerNumber(&bstrNumbers[7]);
					pContact->get_RadioTelephoneNumber(&bstrNumbers[8]);
					if (g_dwIncludeFax)
					{
						pContact->get_BusinessFaxNumber(&bstrNumbers[9]);
						pContact->get_HomeFaxNumber(&bstrNumbers[10]);
					}

					if ((g_szContactFilter != NULL) || (g_bIncludeNoCat))
					{
						TCHAR * pCurrCat = NULL;
						TCHAR szMarkers[] = {',',';'};
						bMatchContact = FALSE;
						// run through the categories for this contact
						pCurrCat = _tcstok(bstrCategories, szMarkers);
						if ((pCurrCat == NULL) && g_bIncludeNoCat)
							bMatchContact = TRUE;
						while (pCurrCat != NULL)
						{
							// loop through each category in the filter list
							TCHAR * pCurrFilterCat = g_szContactFilter;
							TCHAR * pDelim = wcschr(pCurrFilterCat, ',');
							if (*pCurrCat == _T(' '))
								pCurrCat++;
							int nCatLen = wcslen(pCurrCat);
							int nFilterLen = 0;
							while(TRUE)
							{
								nFilterLen = (pDelim != NULL) ? ((pDelim - pCurrFilterCat)) : wcslen(pCurrFilterCat);
								if (nCatLen == nFilterLen)
								{
									if (_wcsnicmp(pCurrCat, pCurrFilterCat, nCatLen) == 0)
									{
										bMatchContact = TRUE;
										break;
									}
								}
								if (pDelim == NULL)
									break;
								pCurrFilterCat = pDelim+1;
								if (*pCurrFilterCat == ' ')
									pCurrFilterCat++;
								pDelim = wcschr(pCurrFilterCat, ',');
							}
							if (bMatchContact)
								break;
							pCurrCat = _tcstok(NULL, szMarkers);
						}
					}

					for (int x=0; x < nMaxNumbers; x++)
					{
						if (bMatchContact && (SysStringByteLen(bstrNumbers[x]) > 0))
						{
							if (nNumberCount < olPhoneList->count)
							{
#ifdef DEBUG_DETAIL
								sprintf(szLog, "Adding number %ls, type %i, from contact %i", bstrNumbers[x], x, nCurrOlRecord);
								LogString(szLog, DEBUG_CONTACT_DETAIL);
#endif
								olPhoneList->entries[nNumberCount].type = x;
								olPhoneList->entries[nNumberCount].index = nCurrOlRecord;
								nNumberCount++;
							}
						}
						SysFreeString(bstrNumbers[x]);
					}
					// clean up
					SysFreeString(bstrCategories);
					pContact->Release();
				}
				nCurrOlRecord++;
			}
			pItemCol->Release();
		}
	}
	if (nNumberCount < olPhoneList->count)
	{
		PHONELIST * pNewPhoneList = (PHONELIST *) LocalReAlloc(olPhoneList, sizeof(PHONELIST) + (nNumberCount * sizeof(PBITEM)), LPTR);
		if (pNewPhoneList != NULL)
		{
#ifdef DEBUG
			sprintf(szLog, "Shrunk size of list to %i entries: size=%8.8X, new=%8.8X", nNumberCount, LocalSize(pNewPhoneList), pNewPhoneList);
			LogString(szLog, DEBUG_CONTACT_DETAIL);
#endif
			olPhoneList = pNewPhoneList;
			olPhoneList->count = nNumberCount;
		}
	}
	olPhoneList->time = GetTickCount();
	if ((olPhoneList->time - dwStartTime) < (g_dwPBCountTimeout * 1000))
		olPhoneList->time = 0;
	// nLastReadOLEntry = 0; //FIX: 1.20: No longer used
	return nNumberCount;
}

int ReadContacts(int nRecord, int nEnd)
{
	CHAR szReply[1024];

	int nCurrOlRecord = 0;
	int nCurrRecord = nRecord;

	if (olPhoneList == NULL)
		CountContacts();
	if ((olPhoneList == NULL) || (nRecord > olPhoneList->count))
		return 0;
	// Initialize POOM, get first contact info
	if (InitializeOutlook())
	{
		// Open the Contacts folder
		IPOutlookItemCollection * pItemCol = NULL;

		if (SUCCEEDED(g_pContactsFolder->get_Items(&pItemCol)))
		{
			IContact * pContact = NULL;
			
			while (nCurrRecord <= nEnd)
			{
				BYTE bType = olPhoneList->entries[nCurrRecord-1].type;
				nCurrOlRecord = olPhoneList->entries[nCurrRecord-1].index;
				if (SUCCEEDED(pItemCol->Item (nCurrOlRecord, reinterpret_cast<IDispatch**>(&pContact))))
				{
					BSTR bstrFirstName;
					BSTR bstrLastName;
					BSTR bstrFileAs;
					BSTR bstrNumber;
					TCHAR szNumType[8];
					CHAR cType[11] = {'H','M','W','M','A','W','H','M','M','W','H'};
				
					// grab properties
					pContact->get_FirstName(&bstrFirstName);
					pContact->get_LastName(&bstrLastName);
					pContact->get_FileAs(&bstrFileAs);
					szNumType[0] = 0;
					switch (bType)
					{
						case 0:
							pContact->get_HomeTelephoneNumber(&bstrNumber);
							break;
						case 1:
							pContact->get_MobileTelephoneNumber(&bstrNumber);
							break;
						case 2:
							pContact->get_BusinessTelephoneNumber(&bstrNumber);
							break;
						case 3:
							wcscpy(szNumType, _T("-Car"));
							pContact->get_CarTelephoneNumber(&bstrNumber);
							break;
						case 4:
							pContact->get_AssistantTelephoneNumber(&bstrNumber);
							break;
						case 5:
							wcscpy(szNumType, _T("-Work2"));
							pContact->get_Business2TelephoneNumber(&bstrNumber);
							break;
						case 6:
							wcscpy(szNumType, _T("-Home2"));
							pContact->get_Home2TelephoneNumber(&bstrNumber);
							break;
						case 7:
							wcscpy(szNumType, _T("-Pager"));
							pContact->get_PagerNumber(&bstrNumber);
							break;
						case 8:
							wcscpy(szNumType, _T("-Radio"));
							pContact->get_RadioTelephoneNumber(&bstrNumber);
							break;
						case 9:
							wcscpy(szNumType, _T("-WrkFax"));
							pContact->get_BusinessFaxNumber(&bstrNumber);
							break;
						case 10:
							wcscpy(szNumType, _T("-HomFax"));
							pContact->get_HomeFaxNumber(&bstrNumber);
							break;
						default:
							pContact->get_HomeTelephoneNumber(&bstrNumber);
							break;
					}

					if (SysStringByteLen(bstrNumber) > 0)
					{
						TCHAR szTemp[32];
						// CHAR szUtfFirst[64];
						// CHAR szUtfLast[64];
						// CHAR szUtfFileAs[128];
						int nType = 129;
						StripPhoneNumber(bstrNumber, szTemp, 32);
						if (szTemp[0] == '+')
							nType = 145;
						else if (szTemp[0] == '1')
							nType = 161;
						/*
						if (g_wConvertToCharSet == CHARSET_UTF8)
						{
							if (SysStringByteLen(bstrFileAs) > 0)
							{
								WideCharToMultiByte(CP_UTF8, 0, bstrFileAs, -1, szUtfFileAs, sizeof(szUtfFileAs), NULL, NULL);
								sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s%ls/%c\"",
									nCurrRecord, szTemp, nType, szUtfFileAs, szNumType, cType[bType]);
							}
							else
							{
								WideCharToMultiByte(CP_UTF8, 0, bstrFirstName, -1, szUtfFirst, sizeof(szUtfFirst), NULL, NULL);
								WideCharToMultiByte(CP_UTF8, 0, bstrLastName, -1, szUtfLast, sizeof(szUtfLast), NULL, NULL);
								sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s %s%ls/%c\"",
									nCurrRecord, szTemp, nType, szUtfFirst, szUtfLast, szNumType, cType[bType]);
							}
						}
						else // if (g_wConvertToCharSet == CHARSET_UCS2)
						{
						*/
						TCHAR szUniName[130];
						CHAR szMappedName[516];
						WORD wLen = 0;
						if (SysStringByteLen(bstrFileAs) > 0)
						{
							// g_dwMaxPBName is set in RegSettings to min(RegValue, 128)
							wcsncpy(szUniName, bstrFileAs, g_dwMaxPBName);
							szUniName[g_dwMaxPBName] = 0;
						}
						else
						{
							if (g_dwFeatureMask & FM_LAST_NAME_FIRST)
							{
								wLen = wcslen(bstrLastName);
								wcsncpy(szUniName, bstrLastName, g_dwMaxPBName);
								if (wLen < g_dwMaxPBName-2)
								{
									wcscat(szUniName, _T(" "));
									wcsncat(szUniName, bstrFirstName, (g_dwMaxPBName - (wLen+2)));
								}
							}
							else
							{
								wLen = wcslen(bstrFirstName);
								wcsncpy(szUniName, bstrFirstName, g_dwMaxPBName);
								if (wLen < g_dwMaxPBName-2)
								{
									wcscat(szUniName, _T(" "));
									wcsncat(szUniName, bstrLastName, (g_dwMaxPBName - (wLen+2)));
								}
							}
							szUniName[g_dwMaxPBName] = 0;
						}
						wLen = wcslen(szUniName);
						if ((szNumType[0] != 0) && (wLen+wcslen(szNumType) < g_dwMaxPBName))
						{
							wcsncat(szUniName, szNumType, (127 - wLen));
							wLen += wcslen(szNumType);
						}
						
						if (wLen < (g_dwMaxPBName-3))
						{
							TCHAR szNumType[4];
							wsprintf(szNumType, _T("/%c"), cType[bType]);
							wcscat(szUniName, szNumType);
						}
						if (g_wConvertToCharSet == CHARSET_UCS2)
						{
							MapToUcs2(szMappedName, szUniName, sizeof(szMappedName));
							sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s\"",
								nCurrRecord, szTemp, nType, szMappedName);
						}
						else if (g_wConvertToCharSet == CHARSET_UTF8)
						{
							WideCharToMultiByte(CP_UTF8, 0, szUniName, -1, szMappedName, sizeof(szMappedName), NULL, NULL);
							sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s\"",
								nCurrRecord, szTemp, nType, szMappedName);
						}
						else // CHARSET_GSM
						{
							MapToGSM(szMappedName, szUniName, sizeof(szMappedName), FALSE);
							sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s\"",
								nCurrRecord, szTemp, nType, szMappedName);
						}
						/*
						}
						// if (g_wConvertToCharSet == CHARSET_GSM)
						{
							if (SysStringByteLen(bstrFileAs) > 0)
							{
								sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%ls%ls/%c\"",
									nCurrRecord, szTemp, nType, bstrFileAs, szNumType, cType[bType]);
							}
							else
							{
								sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%ls %ls%ls/%c\"",
									nCurrRecord, szTemp, nType, bstrFirstName, bstrLastName, szNumType, cType[bType]);
							}
						}
						*/
						AddLineToResponse(szReply, FALSE);
					}
					SysFreeString(bstrNumber);
					// clean up
					SysFreeString(bstrFirstName);
					SysFreeString(bstrLastName);
					SysFreeString(bstrFileAs);
					pContact->Release();
				}
				nCurrRecord++;
			}
			pItemCol->Release();
		}
	}
	return nEnd;
}

int ReadCallContacts(int nRecord, int nEnd, int nType)
{
	CHAR szReply[160];
	HANDLE hPhone;
	HRESULT hr = PhoneOpenCallLog(&hPhone);

	int nCurr = nRecord;
	int nNumRequested = nEnd - nRecord + 1; // how many records to return
	int nNumReturned = 0;
	// Initialize call log
	if (hr == S_OK)
	{
		CALLLOGENTRY entry;
		DWORD dwCount = 0;
		int iIOM = (nType == CONTACTS_MC) ? IOM_MISSED : (nType == CONTACTS_RC) ? IOM_INCOMING : IOM_OUTGOING;
		if ((LogList != NULL) && (LogList->IOM != iIOM))
		{
			nLastReadSIMEntry = -1;
			DeleteLogList();
		}
		hr = PhoneSeekCallLog(hPhone, CALLLOGSEEK_BEGINNING, nLastReadSIMEntry + 1, &dwCount);
		while (nNumReturned < nNumRequested)
		{
			memset(&entry, 0, sizeof(entry));
			entry.cbSize = sizeof(entry);
			hr = PhoneGetCallLogEntry(hPhone, &entry);
			if (hr == S_OK)
			{
				nLastReadSIMEntry++;
				if (entry.iom == iIOM && entry.pszNumber != NULL)
				{
					if (!FindLogEntry(entry.pszNumber))
					{
						TCHAR szTemp[32];
						TCHAR szTime[32];
						TCHAR szEmpty[] = {0};
						// TCHAR szName[64];
						CHAR  szNameX[260];
						SYSTEMTIME st;
						FileTimeToSystemTime(&entry.ftStartTime, &st);
						DWORD dwType = 129;
						StripPhoneNumber(entry.pszNumber, szTemp, 32);
						if (szTemp[0] == '+')
							dwType = 145;
						else if (szTemp[0] == '1')
							dwType = 161;

						memset(szNameX, 0, sizeof(szNameX));
						if (entry.pszName != NULL)
						{
							if (g_wConvertToCharSet == CHARSET_UCS2)
							{
								MapToUcs2(szNameX, entry.pszName, sizeof(szNameX));
							}
							else if (g_wConvertToCharSet == CHARSET_UTF8)
							{
								WideCharToMultiByte(CP_UTF8, 0, entry.pszName, -1, szNameX, sizeof(szNameX), NULL, NULL);
							}
							else // CHARSET_GSM
							{
								MapToGSM(szNameX, entry.pszName, sizeof(szNameX), FALSE);
								/*
								TCHAR szName[64];
								wcsncpy(szName, entry.pszName, 64);
								sprintf(szNameX, "%ls", szName);
								*/
							}
						}

						wsprintf(szTime, _T("%i/%2.2i/%2.2i,%2.2i:%2.2i"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);
						sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s\",\"%ls\"",
							nCurr++, szTemp, dwType, szNameX, szTime);
						AddLineToResponse(szReply, FALSE);
						AddLogEntry(entry.pszNumber, entry.iom);
						nNumReturned++;
					}
				}
				if (entry.pszNumber != NULL)
					LocalFree(entry.pszNumber);
				if (entry.pszName != NULL)
					LocalFree(entry.pszName);
				if (entry.pszNameType != NULL)
					LocalFree(entry.pszNameType);
				if (entry.pszNote != NULL)
					LocalFree(entry.pszNote);
			}
			else
			{
				// reached end of log
#ifdef DEBUG
				sprintf(szReply, "PhoneGetCallLogEntry for %i returned %8.8X", nCurr, hr);
				LogString(szReply, DEBUG_CONTACTS);
#endif
				break;
			}
		}
		PhoneCloseCallLog(hPhone);
	}
	return nNumReturned;
}

int ReadSIMContacts(DWORD dwLoc, int nRecord, int nEnd)
{
	CHAR szReply[160];

	int nCurr = nRecord;
	if (nCurr == 0)
		nCurr++;
	// Initialize SIM
	if (InitializeSIM())
	{
		SIMPHONEBOOKENTRY entry;
		
		while (nCurr <= nEnd)
		{
			if (SUCCEEDED(SimReadPhonebookEntry(g_hSIM, dwLoc, nCurr, &entry)))
			{
				DWORD dwType = (entry.dwAddressType == SIM_ADDRTYPE_INTERNATIONAL) ? 145 : 129;
				CHAR szNameX[(MAX_LENGTH_PHONEBOOKENTRYTEXT+1)*4];
				memset(szNameX, 0, sizeof(szNameX));
				if (entry.lpszText[0] != 0)
				{
					if (g_wConvertToCharSet == CHARSET_UCS2)
					{
						MapToUcs2(szNameX, entry.lpszText, sizeof(szNameX));
					}
					else if (g_wConvertToCharSet == CHARSET_UTF8)
					{
						WideCharToMultiByte(CP_UTF8, 0, entry.lpszText, -1, szNameX, sizeof(szNameX), NULL, NULL);
					}
					else // CHARSET_GSM
					{
						TCHAR szName[MAX_LENGTH_PHONEBOOKENTRYTEXT];
						wcsncpy(szName, entry.lpszText, MAX_LENGTH_PHONEBOOKENTRYTEXT-1);
						sprintf(szNameX, "%ls", szName);
					}
				}
				sprintf(szReply, "+CPBR: %i,\"%ls\",%i,\"%s\"",
					nCurr, entry.lpszAddress, dwType, szNameX);
				AddLineToResponse(szReply, FALSE);
			}
			nCurr++;
		}
	}
	return nCurr;
}